package com.yeskery.nut.script;

import com.yeskery.nut.core.Method;
import com.yeskery.nut.core.NutException;
import com.yeskery.nut.core.Path;
import com.yeskery.nut.script.function.FunctionExecutor;
import com.yeskery.nut.script.parser.FileScriptMetadataReader;
import com.yeskery.nut.script.parser.ScriptMetadataParser;
import com.yeskery.nut.script.parser.ScriptMetadataReader;

import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 脚本上下文空间
 * @author sprout
 * 2022-05-14 14:40
 */
public class FileScriptContext extends AbstractScriptContext {

    /**
     * 构建脚本上下文空间
     * @param scriptMetadataParser 脚本转换器
     * @param charset 字符编码
     * @param paths 配置文件路径
     */
    public FileScriptContext(ScriptMetadataParser scriptMetadataParser, Charset charset, String... paths) {
        for (String path : paths) {
            scriptMetadataReaders.add(new FileScriptMetadataReader(scriptMetadataParser, charset, path));
        }
    }

    /**
     * 构建脚本上下文空间
     * @param scriptMetadataParser 脚本转换器
     * @param paths 配置文件路径
     */
    public FileScriptContext(ScriptMetadataParser scriptMetadataParser, String... paths) {
        for (String path : paths) {
            scriptMetadataReaders.add(new FileScriptMetadataReader(scriptMetadataParser, path));
        }
    }

    @Override
    public List<ControllerScript> getControllerScripts() {
        return getControllerScripts(functionExecutor);
    }

    @Override
    public List<ControllerScript> getControllerScripts(FunctionExecutor functionExecutor) {
        List<ControllerScript> controllerScriptList = new LinkedList<>();
        List<Metadata> allScriptMetadata = new LinkedList<>();
        for (ScriptMetadataReader scriptMetadataReader : scriptMetadataReaders) {
            allScriptMetadata.addAll(scriptMetadataReader.parseMetadata());
        }

        // 脚本合并
        List<Map<Method, Metadata>> mergeMetadataList = mergeMetadata(allScriptMetadata);

        for (int i = 0; i < mergeMetadataList.size(); i++) {
            Map<Method, Metadata> methodMetadataMap = mergeMetadataList.get(i);
            ControllerScript controllerScript = new MetadataControllerScript(i + 1, methodMetadataMap, functionExecutor);
            controllerScriptList.add(controllerScript);
        }
        return controllerScriptList;
    }

    /**
     * 元数据合并
     * @param allScriptMetadata 所有脚本元数据
     * @return 合并后的元数据脚本
     */
    private List<Map<Method, Metadata>> mergeMetadata(List<Metadata> allScriptMetadata) {
        List<Map<Method, Metadata>> methodMetadataMapList = new LinkedList<>();
        Map<Path, List<Metadata>> pathListMap = allScriptMetadata.stream()
                .collect(Collectors.groupingBy(m -> m.getPaths().stream()
                        .findFirst().orElseThrow(() -> new NutException("Script Path Missing."))));
        for (Map.Entry<Path, List<Metadata>> entry : pathListMap.entrySet()) {
            Map<Method, Metadata> methodMetadataMap = new HashMap<>(16);
            for (Metadata metadata : entry.getValue()) {
                for (Method method : metadata.getMethods()) {
                    methodMetadataMap.put(method, metadata);
                }
            }
            methodMetadataMapList.add(methodMetadataMap);
        }
        return methodMetadataMapList;
    }
}
